接下來把開發的 Rust/Axum API 打包成可重現的容器影像,搭配 docker-compose 啟動 Postgres、Redis 以及 API,方便在開發/測試或小型生產環境快速部署。
這個範例假設你的 Cargo.toml、src/、migrations/ 在專案目錄。程式名稱以 cargo 的 package.name(例如 sqlx_connect_demo)為準。
Dockerfile
# ---- Build stage ----
FROM rust:1.88 as builder
WORKDIR /usr/src/app
# 安裝 musl 工具鏈與必要套件
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential musl-tools musl-dev pkg-config libssl-dev ca-certificates \
 && rm -rf /var/lib/apt/lists/*
RUN rustup target add x86_64-unknown-linux-musl
# 複製 Cargo 檔以利用 Docker cache
COPY Cargo.toml Cargo.lock ./
# 建一個暫時的 main.rs 以便下載依賴(cache trick)
RUN mkdir -p src
RUN echo 'fn main() { println!("dummy"); }' > src/main.rs
# 預先抓取依賴
RUN cargo fetch
# 複製專案檔案(不複製 target,因為 target 的檔案大,複製又慢又久,再加上內容都是可下載的依賴套件)
COPY src ./src
COPY migrations ./migrations
COPY tests ./tests
COPY .env .env
# 以 musl target 建置 release binary
RUN cargo build --release --target x86_64-unknown-linux-musl
# ---- Runtime stage(使用 scratch) ----
FROM scratch
# 請把 sqlx_connect_demo 換成你 Cargo.toml 裡的 package.name
COPY --from=builder /usr/src/app/target/x86_64-unknown-linux-musl/release/sqlx_connect_demo /sqlx_connect_demo
# 複製 CA certs(如果程式需要 TLS)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
EXPOSE 3000
ENTRYPOINT ["/sqlx_connect_demo"]
說明:
以下範例在同一個 compose network 內啟動三個服務:db、redis、api。檔名 docker-compose.yml:
services:
  db:
    image: postgres:18
    restart: unless-stopped
    environment:
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD: myapp_pass
      POSTGRES_DB: myapp_db
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 10
    networks:
      - mynet
  redis:
    image: redis:8
    restart: unless-stopped
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - mynet
  api:
    build:
      context: .
      dockerfile: Dockerfile
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped
    environment:
      DATABASE_URL: postgresql://myapp:myapp_pass@db:5432/myapp_db
      REDIS_URL: redis://redis:6379/
      JWT_SECRET: change_me_in_production
      RUST_LOG: info
    ports:
      - "3000:3000"
    networks:
      - mynet
volumes:
  db_data:
  redis_data:
networks:
  mynet:
    driver: bridge
說明重點:
我們先前的程式碼誤用了 macro! 這將會造成 build 錯誤,請把 handles delete_user 的 macro! 修改成:
let res = sqlx::query(
		r#"
		DELETE FROM users
		WHERE id = $1 AND caller_id = $2
		"#,
	)
	.bind(id)
	.bind(caller_id)
	.execute(&pool)
	.await
	.map_err(|e| internal_err(e))?;
另外,main.rs 中的 127.0.0.1 讓我們只能在 docker 容器內通訊,必須修改成 0.0.0.0,這樣暴露接口才能從外部連接。
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
把 Dockerfile 和 docker-compose.yml 放在跟 Cargo.toml 同一個資料夾中,就能開始了。
建立與執行:
local build 並啟動:
docker compose build
[+] Building 116.7s (22/22) FINISHED
 => [internal] load local bake definitions                                                                         0.0s
 => => reading from stdin 540B                                                                                     0.0s
 => [internal] load build definition from Dockerfile                                                               0.1s
 => => transferring dockerfile: 1.49kB                                                                             0.0s
 => WARN: FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 2)                                     0.1s
 => [internal] load metadata for docker.io/library/rust:1.88                                                       1.5s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [builder  1/13] FROM docker.io/library/rust:1.88@sha256:af306cfa71d987911a781c37b59d7d67d934f49684058f96cf720  0.0s
 => [internal] load build context                                                                                  0.0s
 => => transferring context: 13.59kB                                                                               0.0s
 => CACHED [builder  2/13] WORKDIR /usr/src/app                                                                    0.0s
 => CACHED [builder  3/13] RUN apt-get update && apt-get install -y --no-install-recommends     build-essential m  0.0s
 => CACHED [builder  4/13] RUN rustup target add x86_64-unknown-linux-musl                                         0.0s
 => CACHED [builder  5/13] COPY Cargo.toml Cargo.lock ./                                                           0.0s
 => CACHED [builder  6/13] RUN mkdir -p src                                                                        0.0s
 => CACHED [builder  7/13] RUN echo 'fn main() { println!("dummy"); }' > src/main.rs                               0.0s
 => CACHED [builder  8/13] RUN cargo fetch                                                                         0.0s
 => [builder  9/13] COPY src ./src                                                                                 0.1s
 => [builder 10/13] COPY migrations ./migrations                                                                   0.0s
 => [builder 11/13] COPY tests ./tests                                                                             0.0s
 => [builder 12/13] COPY .env .env                                                                                 0.0s
 => [builder 13/13] RUN cargo build --release --target x86_64-unknown-linux-musl                                 113.1s
 => [stage-1 1/2] COPY --from=builder /usr/src/app/target/x86_64-unknown-linux-musl/release/sqlx_connect_demo /sq  0.1s
 => [stage-1 2/2] COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt        0.1s
 => exporting to image                                                                                             0.3s
 => => exporting layers                                                                                            0.2s
 => => writing image sha256:4c6bd3f093bbe92b20768bb7a45969d471e83477a715ebdc848b140beb18824f                       0.0s
 => => naming to docker.io/library/sqlx_connect_demo-api                                                           0.0s
 => resolving provenance for metadata file                                                                         0.0s
[+] Building 1/1
 ✔ sqlx_connect_demo-api  Built                                                                                    0.0s
docker compose up(關閉視窗或按下 Ctrl + C,程式就會結束)
[+] Running 2/3
 ✔ Container sqlx_connect_demo-db-1     Running                                                                    0.0s[+] Running 3/3
 ✔ Container sqlx_connect_demo-db-1     Running                                                                    0.0s
 ✔ Container sqlx_connect_demo-redis-1  Running                                                                    0.0s
 ✔ Container sqlx_connect_demo-api-1    Recreated                                                                  0.1s
Attaching to api-1
api-1  | 成功建立 PgPool
api-1  | 2025-10-10T01:47:13.461193Z  INFO relation "_sqlx_migrations" already exists, skipping
api-1  | 成功完成 migrations
api-1  | 2025-10-10T01:47:20.711707Z  INFO finished processing request latency=0 ms status=303
api-1  | 2025-10-10T01:47:20.732604Z  INFO finished processing request latency=0 ms status=200
api-1  | 2025-10-10T01:47:20.804000Z  INFO finished processing request latency=0 ms status=200
api-1  | 2025-10-10T01:47:20.806666Z  INFO finished processing request latency=0 ms status=200
api-1  | 2025-10-10T01:47:20.825082Z  INFO finished processing request latency=0 ms status=200
api-1  | 2025-10-10T01:47:20.840708Z  INFO finished processing request latency=0 ms status=200
api-1  | 2025-10-10T01:47:20.863574Z  INFO finished processing request latency=0 ms status=200
api-1  | 2025-10-10T01:47:20.980924Z  INFO finished processing request latency=0 ms status=200
api-1  | 2025-10-10T01:47:21.442509Z  INFO finished processing request latency=0 ms status=200
後台模式(不必一直開啟視窗):
檢查 log:
停止:
停止並移除(-v 會刪除 volumes):